﻿using Awesomium.Core;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace LinkedInApi
{
    partial class ApiClient
    {
        private static class OAuth2
        {
            private static readonly string _loginInput_id = "session_key-oauth2SAuthorizeForm";
            private static readonly string _passwordInput_id = "session_password-oauth2SAuthorizeForm";
            private static readonly string _authorizeSubmit_name = "authorize";
            private static readonly string _globalErrorDiv_id = "global-error";
            private static readonly string _loginSpanError_id = "session_key-oauth2SAuthorizeForm-error";
            private static readonly string _passSpanError_id = "session_password-oauth2SAuthorizeForm-error";

            private static ApiError lastError = null;

            public static ApiError LastError
            {
                get
                {
                    return OAuth2.lastError;
                }
            }

            public static bool HasError
            {
                get
                {
                    return OAuth2.lastError != null;
                }
            }

            public static async Task<string> GetAuthorizationCode(string clientID, string redirectUri, string login, string password, string scope, ILogger logger, int timeout)
            {
                logger.Log("OAuth2", "GetAuthorizationCode() start");
                

                string authorizationCode = "";
                OAuth2.lastError = null;

                var state = Guid.NewGuid().ToString();

                var url = String.Format(
                    "https://www.linkedin.com/uas/oauth2/authorization?response_type=code&client_id={0}&redirect_uri={1}&state={2}{3}"
                    , clientID
                    , redirectUri
                    , state
                    , scope).ToUri();

                var task = new Task(() =>
                {
                    logger.Log("OAuth2", "WebCore.Initialize()");
                    WebCore.Initialize(new WebConfig(), true);

                    logger.Log("OAuth2", "WebCore.CreateWebView()");

                    using (WebView web = WebCore.CreateWebView(640, 480, WebViewType.Window))
                    {
                        logger.Log("OAuth2", "WebView created");

                        web.ConsoleMessage += (s, e) =>
                        {
                            logger.Log("WebView", e.Message);
                        };
                        
                        web.DocumentReady += (s, e) =>
                        {
                            if (OAuth2.HasError || !String.IsNullOrWhiteSpace(authorizationCode))
                                return;

                            if (!String.IsNullOrWhiteSpace(e.OriginalString) && e.OriginalString.StartsWith(redirectUri))
                            {
                                logger.Log("WebView", "Redirected to {0}", e.OriginalString);
                                var pars = e.Url.ParseQueryString();

                                if (pars.AllKeys.Any(k => k == "error" || k == "error_description"))
                                {
                                    OAuth2.lastError = new ApiError(pars["error"], pars["error_description"]);
                                    logger.Log("WebView", "API error - {0}", OAuth2.lastError);
                                }
                                else if (pars["state"] == state)
                                {
                                    authorizationCode = pars["code"];
                                    logger.Log("WebView", "Code received - {0}", authorizationCode);

                                }

                                logger.Log("OAuth2", "WebCore.Shutdown()");
                                WebCore.Shutdown();
                            }
                            else if (e.ReadyState == DocumentReadyState.Loaded)
                            {
                                logger.Log("WebView", "Document loaded");
                                var global = e.Environment;

                                if (global)
                                {
                                    logger.Log("WebView", "Environment ready, search inputs...");

                                    var globalError = global.document.getElementById(_globalErrorDiv_id);
                                    
                                    if (!globalError)
                                    {
                                        logger.Log("WebView", "global-error Div don`t found");
                                        (s as WebView).Reload(true);
                                    }
                                    else if (globalError.children.length > 0)
                                    {
                                        logger.Log("WebView", "Document global error");
                                        var loginSpan = global.document.getElementById(_loginSpanError_id);
                                        var passSpan = global.document.getElementById(_passSpanError_id);

                                        string error = ":";
                                        if (loginSpan != null)
                                            error += (string)loginSpan.innerText;
                                        if (passSpan != null)
                                            error += (error.Length > 1 ? ";" : "") + (string)passSpan.innerText;

                                        error = (string)globalError.innerText + (error.Length > 1 ? error : "");

                                        OAuth2.lastError = new ApiError("Page error", error);

                                        logger.Log("WebView", OAuth2.lastError.ToString());
                                        logger.Log("OAuth2", "WebCore.Shutdown()");
                                        WebCore.Shutdown();
                                    }
                                    else
                                    {
                                        var loginInput = global.document.getElementById(_loginInput_id);
                                        var passwordInput = global.document.getElementById(_passwordInput_id);
                                        var submitInput = global.document.getElementsByName(_authorizeSubmit_name)[0];

                                        if (loginInput && passwordInput && submitInput)
                                        {
                                            logger.Log("WebView", "Fill Inputs");
                                            loginInput.value = login;
                                            passwordInput.value = password;

                                            logger.Log("WebView", "Click submit");
                                            submitInput.click();
                                        }
                                        else
                                        {
                                            logger.Log("WebView", "Input state error: loginInput - {0}, passwordInput - {1}, submitInput - {2}"
                                                , loginInput ? "ok" : "null"
                                                , passwordInput ? "ok" : "null"
                                                , submitInput ? "ok" : "null");
                                        }
                                    }
                                }
                            }
                        };

                        logger.Log("OAuth2", "WebView.Source={0}", url);
                        web.Source = url;

                        logger.Log("OAuth2", "WebCore.Run()");
                        WebCore.Run();
                        logger.Log("OAuth2", "WebCore end run");
                    }
                });
                
                task.Start();
                if (await Task.WhenAny(task, Task.Delay(timeout)) == task)
                {
                    if (OAuth2.HasError)
                    {
                        logger.Log("OAuth2", "End with error");
                    }
                    else
                    {
                        logger.Log("OAuth2", "End successful");
                        return authorizationCode;
                    }    
                }
                else
                {
                    OAuth2.lastError = new ApiError(new TimeoutException("Timeout expired"));
                    logger.Log("OAuth2", OAuth2.lastError.ToString());
                    logger.Log("OAuth2", "WebCore.Shutdown()");
                    WebCore.Shutdown();
                }

                return "";
            }
        }
    }
}
